
#include <BOpenGL>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BViewer>
#include <BCamera>
#include <BRender>
#include <BSpacer>
#include <BPainter>
#include <BFont>
#include <BLayout>
#include <BScene>
#include <BMessageBox>
#include <stdio.h>

#include "PaintHandler.h"

PaintHandler::PaintHandler()
{
	this->setName("Carver");

	_brushSize = 50;
	_pointVisible = false;
	_pressed = false;
	_shiftDown = false;
	_ctrlDown = false;
	_altDown = false;
	_brushColor.set(200, 0, 0);
	_brushCircle.setSides(24);

	connect(this, Signal_AssetChanged, &PaintHandler::slotAssetChanged);
}
PaintHandler::~PaintHandler()
{

}

void PaintHandler::setBrushColor(const BColor& color)
{
	if (_brushColor != color)
	{
		_brushColor = color;
		this->fresh();
	}
}
const BColor& PaintHandler::brushColor() const
{
	return _brushColor;
}

void PaintHandler::setBrushImage(BImage* brushImage)
{
	_brushImage = brushImage;
}
BImage* PaintHandler::brushImage() const
{
	return _brushImage;
}

void PaintHandler::setBrushSize(int brushSize)
{
	if (_brushSize != brushSize)
	{
		_brushSize = brushSize;
		this->fresh();
	}
}
int PaintHandler::brushSize() const
{
	return _brushSize;
}

void PaintHandler::setPointVisible(bool pointVisible)
{
	if (_pointVisible != pointVisible)
	{
		_pointVisible = pointVisible;
	}
}
bool PaintHandler::pointVisible() const
{
	return _pointVisible;
}

bool PaintHandler::intersectTest(const BQuad& quad, const BTriangle& trig)
{
	if (quad.normal().dot(trig.normal()) < 0)
		return false;
	if (quad.around(trig.a()) || quad.around(trig.b()) || quad.around(trig.c()))
	{
		return true;
	}
	if (trig.around(quad.a()))
		return true;
	if (trig.around(quad.b()))
		return true;
	if (trig.around(quad.c()))
		return true;
	if (trig.around(quad.d()))
		return true;
	return false;
}
void PaintHandler::catchPixels(const BQuad& quad)
{

}
void PaintHandler::paintPixels(const BQuad& quad)
{

}

bool IntersectCoord(const BCoord& a, const BCoord& b, const BCoord& c, float& lena, float& lenb)
{
	float len2 = c.length2();
	if (len2 <= 1e-8)
	{
		lena = 0;
		lenb = 0;
		return true;
	}
	float len = sqrt(len2);
	float A = c.dot(a);
	float cosa = A / len;
	float sina = sqrt(len2 - A * A) / len;
	float B = c.dot(b);
	float cosb = B / len;
	float sinb = sqrt(len2 - B * B) / len;
	float D = sina * cosb + cosa * sinb;
	if (bZero(D))
		return false;
	lena = sinb / D * len;
	lenb = sina / D * len;
	return true;
}
bool ContainCoord(const BCoord& a, const BCoord& b, const BCoord& c, const BCoord& d)
{
	BCoord da = a - d;
	BCoord db = b - d;
	BCoord dc = c - d;
	float dab = da.cross(db);
	float dbc = db.cross(dc);
	float dca = dc.cross(da);
	if (dab >= 0 && dbc >= 0 && dca >= 0)
		return true;
	if (dab <= 0 && dbc <= 0 && dca <= 0)
		return true;
	return false;
}
bool IntersectQuad(const BQuad& quad, const BQuad& other)
{
	BVector p;
	BVector ab = quad.b() - quad.a();
	BVector bc = quad.c() - quad.b();
	BVector cd = quad.d() - quad.c();
	BVector da = quad.a() - quad.d();
	BPlane pab(ab.cross(quad.normal()), quad.a());
	BPlane pbc(bc.cross(quad.normal()), quad.b());
	BPlane pcd(cd.cross(quad.normal()), quad.c());
	BPlane pda(da.cross(quad.normal()), quad.d());
	{
		BLine olab(other.a(), other.b());
		if (olab.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olab.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olab.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olab.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	{
		BLine olbc(other.b(), other.c());
		if (olbc.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olbc.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olbc.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olbc.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	{
		BLine olcd(other.c(), other.d());
		if (olcd.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olcd.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olcd.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olcd.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	{
		BLine olda(other.d(), other.a());
		if (olda.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olda.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olda.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olda.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	return false;
}

void PaintHandler::slotAssetChanged(BObject* object, const BValue& value)
{
	_terrain = (BTerrain*)value;
	if (_terrain)
	{
		const BVectorArray* vertices = _terrain->vertices();
		const BVectorArray* normals = _terrain->normals();
		const BIntArray* indices = _terrain->indices();
		_pointInfos.resize(vertices->size());
		for (int i = 0; i < indices->size(); i += 4)
		{
			int fi = i / 4;
			int ia = indices->at(i + 0);
			int ib = indices->at(i + 1);
			int ic = indices->at(i + 2);
			int id = indices->at(i + 3);
			_pointInfos[ia].links.insert(ib);
			_pointInfos[ia].links.insert(id);
			_pointInfos[ib].links.insert(ia);
			_pointInfos[ib].links.insert(ic);
			_pointInfos[ic].links.insert(ib);
			_pointInfos[ic].links.insert(id);
			_pointInfos[id].links.insert(ia);
			_pointInfos[id].links.insert(ic);
			_pointInfos[ia].faces.insert(fi);
			_pointInfos[ib].faces.insert(fi);
			_pointInfos[ic].faces.insert(fi);
			_pointInfos[id].faces.insert(fi);
		}

		BImage* image = dynamic_cast<BImage*>(_terrain->texture());
		const BCoordArray* coords = _terrain->texCoords();
		_pixelPoints.resize(image->width() * image->height());
		_pixelRadius.resize(image->width() * image->height());
		_facePixels.resize(indices->size() / 4);
		for (int i = 0; i < indices->size(); i += 4)
		{
			int fi = i / 4;
			int ia = indices->at(i + 0);
			int ib = indices->at(i + 1);
			int ic = indices->at(i + 2);
			int id = indices->at(i + 3);
			const BVector& va = vertices->at(ia);
			const BVector& vb = vertices->at(ib);
			const BVector& vc = vertices->at(ic);
			const BVector& vd = vertices->at(id);
			const BCoord& ta = coords->at(ia);
			const BCoord& tb = coords->at(ib);
			const BCoord& tc = coords->at(ic);
			const BCoord& td = coords->at(id);
			BVector vab = vb - va;
			BVector vac = vc - va;
			BVector vad = vd - va;
			BCoord tab = tb - ta;
			BCoord tac = tc - ta;
			BCoord tad = td - ta;
			BCoord nab = tab.normal();
			BCoord nac = tac.normal();
			BCoord nad = tad.normal();
			float txmin = bMin(bMin(ta.x(), tb.x()), bMin(tc.x(), td.x()));
			float txmax = bMax(bMax(ta.x(), tb.x()), bMax(tc.x(), td.x()));
			float tymin = bMin(bMin(ta.y(), tb.y()), bMin(tc.y(), td.y()));
			float tymax = bMax(bMax(ta.y(), tb.y()), bMax(tc.y(), td.y()));
			int cxmin = (int)round(txmin * image->width());
			int cxmax = (int)round(txmax * image->width());
			int cymin = (int)round(tymin * image->height());
			int cymax = (int)round(tymax * image->height());
			BReal txstep = BReal(1.0) / image->width();
			BReal tystep = BReal(1.0) / image->height();
			BReal cxhalf = txstep * BReal(0.5);
			BReal cyhalf = tystep * BReal(0.5);
			BReal rab = vab.length() / (tab.length() * image->height());
			BReal rad = vad.length() / (tad.length() * image->width());
			BVector nabc = (normals->at(ia) + normals->at(ib) + normals->at(ic)).normal();
			BVector nacd = (normals->at(ia) + normals->at(ic) + normals->at(id)).normal();
			for (int cy = cymin; cy < cymax; cy++)
			{
				for (int cx = cxmin; cx < cxmax; cx++)
				{
					int pi = cy * image->width() + cx;
					BCoord tv(cx * txstep + cxhalf, cy * tystep + cyhalf);
					if (ContainCoord(ta, tb, tc, tv))
					{
						float lenb, lenc;
						if (IntersectCoord(nab, nac, tv - ta, lenb, lenc))
						{
							BReal ratioAB = lenb / tab.length();
							BReal ratioAC = lenc / tac.length();
							_pixelPoints[pi] = va + vab * ratioAB + vac * ratioAC;
							_pixelRadius[pi] = (rab + rad) * (BReal)0.2;
							_facePixels[fi].append(pi);
						}
						continue;
					}
					if (ContainCoord(ta, tc, td, tv))
					{
						float lenc, lend;
						if (IntersectCoord(nac, nad, tv - ta, lenc, lend))
						{
							BReal ratioAC = lenc / tac.length();
							BReal ratioAD = lend / tad.length();
							_pixelPoints[pi] = va + vac * ratioAC + vad * ratioAD;
							_pixelRadius[pi] = (rab + rad) * (BReal)0.2;
							_facePixels[fi].append(pi);
						}
						continue;
					}
				}
			}
		}
	}
}

void PaintHandler::paint(BPainter& painter)
{
	if (_pickHit)
	{
		const BVector& position = this->camera()->position();
		const BVector& center = this->camera()->center();

	}
}
void PaintHandler::render(BRender& render)
{
	render.setLighting(false);
	render.setDepthTest(false);
	if (_brushImage)
	{
		render.setTexture(_brushImage);
		render.fillQuad(_brushQuad);
		render.setTexture(0);
	}
	render.setColor(_brushColor);
	render.drawCircle(_brushCircle);
	
	if (_pointVisible)
	{
		render.setPointSize(3);
		render.setPointSmooth(true);
		render.setColor(255, 100, 0);
		for (int i = 0; i < _facesCatched.size(); i++)
		{
			int fi = _facesCatched[i];
			const BIntArray& pis = _facePixels[fi];
			for (int j = 0; j < pis.size(); j++)
			{
				int pi = pis[j];
				BVector pv = _pixelPoints[pi] * _terrain->matrix();
				render.drawVertex(pv);
			}
		}
	}
}

void PaintHandler::updateEvent(const BEvent& event)
{
	if (_pickHit)
	{
		_facesCatched.reset();
		BMatrix matrix = _terrain->matrix().inverse();
		BQuad quad = _brushQuad * matrix;
		BSpace quadSpace(quad.a());
		quadSpace.expand(quad.b());
		quadSpace.expand(quad.c());
		quadSpace.expand(quad.d());

		BVectorArray* vertices = _terrain->vertices();
		BIntArray* indices = _terrain->indices();
		if (_pickHit)
		{
			int fi = _pickHit->index();
			int ia = indices->at(fi + 0);
			int ib = indices->at(fi + 1);
			int ic = indices->at(fi + 2);
			int id = indices->at(fi + 3);
			static BIntArray expandIndices;
			expandIndices.reset();
			expandIndices.append(ia);
			expandIndices.append(ib);
			expandIndices.append(ic);
			expandIndices.append(id);
			_facesCatched.insert(fi / 4);
			for (int i = 0; i < _pointInfos.size(); i++)
			{
				_pointInfos[i].state = 0;
			}
			for (int i = 0; i < expandIndices.size(); i++)
			{
				int index = expandIndices[i];
				PointInfo& pinfo = _pointInfos[index];
				const BVector& v = vertices->at(index);
				if (quad.around(v))
				{
					pinfo.state = 1;
					_facesCatched.insert(pinfo.faces);
					for (int li = 0; li < pinfo.links.size(); li++)
					{
						int index = pinfo.links[li];
						if (_pointInfos[index].state == 0 && !expandIndices.contain(index))
						{
							expandIndices.append(index);
						}
					}
				}
				else
				{
					for (int j = 0; j < pinfo.faces.size(); j++)
					{
						int fi = pinfo.faces[j];
						int ia = indices->at(fi * 4 + 0);
						int ib = indices->at(fi * 4 + 1);
						int ic = indices->at(fi * 4 + 2);
						int id = indices->at(fi * 4 + 3);
						BSpace space(vertices->at(ia));
						space.expand(vertices->at(ib));
						space.expand(vertices->at(ic));
						space.expand(vertices->at(id));
						if (quadSpace.intersect(space))
						{
							_facesCatched.insert(fi);
						}
					}
					pinfo.state = -1;
				}
			}
		}
	}
	if (_pressed)
	{
		BMatrix matrix = _terrain->matrix().inverse();
		BCircle circle = _brushCircle * matrix;
		BQuad quad = _brushQuad * matrix;
		BVector vecAB = quad.b() - quad.a();
		BReal lenAB = vecAB.length();
		vecAB /= lenAB;
		BVector vecAD = quad.d() - quad.a();
		BReal lenAD = vecAD.length();
		vecAD /= lenAD;

		BImage* image = dynamic_cast<BImage*>(_terrain->texture());
		BByteArray* pixels = image->pixels();
		for (int i = 0; i < _facesCatched.size(); i++)
		{
			int fi = _facesCatched[i];
			const BIntArray& pis = _facePixels[fi];
			for (int j = 0; j < pis.size(); j++)
			{
				int pi = pis[j];
				BVector pv = _pixelPoints[pi];
				BReal ratio = (circle.radius() - (circle.center() - pv).length()) / circle.radius();
				if (ratio < 0)
					continue;
				BLine line(pv - quad.normal() * lenAB, pv + quad.normal() * lenAB);
				BVector point;
				if (!line.intersect(circle.plane(), point))
					continue;
				BColor color = _brushColor;
				if (_brushImage)
				{
					int x = int((point - quad.a()).dot(vecAD) / lenAD * _brushImage->width());
					int y = int((point - quad.a()).dot(vecAB) / lenAB * _brushImage->height());
					if (x < 0 || x >= _brushImage->width())
						continue;
					if (y < 0 || y >= _brushImage->height())
						continue;
					if (_brushImage->format() == Format_RGB)
					{
						BByte* pixel = _brushImage->pixels()->data() + (y * _brushImage->width() + x) * 3;
						color.r() = pixel[0];
						color.g() = pixel[1];
						color.b() = pixel[2];
						color.a() = 255;
					}
					if (_brushImage->format() == Format_RGBA)
					{
						BByte* pixel = _brushImage->pixels()->data() + (y * _brushImage->width() + x) * 4;
						color.r() = pixel[0];
						color.g() = pixel[1];
						color.b() = pixel[2];
						color.a() = pixel[3];
					}
				}
				ratio *= color.a() / (BReal)255.0;
				if (image->format() == Format_RGB)
				{
					BByte* pixel = pixels->data() + pi * 3;
					pixel[0] = BByte(pixel[0] * (1.0 - ratio) + color.r() * ratio);
					pixel[1] = BByte(pixel[1] * (1.0 - ratio) + color.g() * ratio);
					pixel[2] = BByte(pixel[2] * (1.0 - ratio) + color.b() * ratio);
				}
			}
		}
		image->dirty();
	}
}

void PaintHandler::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_LShift || keyEvent.key() == Key_RShift)
	{
		_shiftDown = true;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_LCtrl || keyEvent.key() == Key_RCtrl)
	{
		_ctrlDown = true;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_Space)
	{
	}
}
void PaintHandler::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_LShift || keyEvent.key() == Key_RShift)
	{
		_shiftDown = false;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_LCtrl || keyEvent.key() == Key_RCtrl)
	{
		_ctrlDown = false;
		keyEvent.accept();
	}
}

void PaintHandler::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		_pressed = true;
		mouseEvent.accept();
	}
}
void PaintHandler::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (_pressed)
	{
		_pressed = false;
		mouseEvent.accept();
	}
}
void PaintHandler::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() != Button_Right)
	{
		BPoint pos = mouseEvent.pos();
		if (_pickHit = this->viewer()->pick(pos))
		{
			BVector normal = _pickHit->absoluteNormal();
			BVector center = _pickHit->absolutePoint();
			BCamera* camera = this->camera();
			BReal unit = camera->unit(center);
			BReal brushWidth = unit * _brushSize * (BReal)0.5;
			BReal brushHeight = unit * _brushSize * (BReal)0.5;
			BVector right = (camera->center() - camera->position()).cross(normal);
			right.normalize();
			BVector front = normal.cross(right);
			front.normalize();
			_brushQuad.a() = center - right * brushWidth + front * brushHeight;
			_brushQuad.b() = center - right * brushWidth - front * brushHeight;
			_brushQuad.c() = center + right * brushWidth - front * brushHeight;
			_brushQuad.d() = center + right * brushWidth + front * brushHeight;
			_brushCircle.setCenter(center);
			_brushCircle.setRadius(brushWidth);
			_brushCircle.setNormal(normal);
			this->fresh();
		}
	}
}

void PaintHandler::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	mouseEvent.accept();
}
